package com.ecommerce.dddmodules.infrastructure.events;

import com.ecommerce.dddmodules.sharedkernel.events.ApplicationEvent;
import com.ecommerce.dddmodules.sharedkernel.events.EventBus;
import com.ecommerce.dddmodules.sharedkernel.events.EventSubscriber;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
 * @packageName: infrastructure.events（基础设施.事件包）
 * @className: SimpleEventBus(简单事件总线)
 * @description: 事件总线的最简单实现方式
 * @author: luds
 * @version: v1.0
 * @date: 2021-04/02
 */
public class SimpleEventBus implements EventBus {
	/**
	 * 订阅者存储空间
	 */
	private final Map<String, Set<EventSubscriber>> subscribers = new ConcurrentHashMap<>();

	/**
	 * 发布事件
	 * @param event 事件
	 * @param <E> 限定事件为应用事件
	 */
	@Override
	public <E extends ApplicationEvent> void publish(E event) {
		//如果订阅者存储空间已经包含此事件类型，则依次激活此类型的事件
		if (subscribers.containsKey(event.getType())) {
			subscribers.get(event.getType())
					.forEach(subscriber -> subscriber.onEvent(event));
		}
	}

	/**
	 * 根据类型和订阅者进行事件订阅
	 * @param eventType 事件类型
	 * @param subscriber 订阅者
	 * @param <E> 限定事件为应用事件
	 */
	@Override
	public <E extends ApplicationEvent> void subscribe(String eventType, EventSubscriber subscriber) {
		// 从订阅者存储空间，获得所有此类型的订阅者集合
		Set<EventSubscriber> eventSubscribers = subscribers.get(eventType);
		// 如果此空间没有该类型的订阅者集合，则重新生成一个；放到此类型中
		if (eventSubscribers == null) {
			eventSubscribers = new CopyOnWriteArraySet<>();
			subscribers.put(eventType, eventSubscribers);
		}
		// 然后在此类型的订阅者集合，追加此订阅者
		eventSubscribers.add(subscriber);
	}

	/**
	 * 根据类型和订阅者进行事件退订
	 * @param eventType 事件类型
	 * @param subscriber 订阅者
	 * @param <E> 限定事件为应用事件
	 */
	@Override
	public <E extends ApplicationEvent> void unsubscribe(String eventType, EventSubscriber subscriber) {
		// 如果订阅者存储空间，包含此类型；则获取该类型的订阅者集合，并从集合中移除该订阅者
		if (subscribers.containsKey(eventType)) {
			subscribers.get(eventType).remove(subscriber);
		}
	}
}
